﻿// 引入必要的命名空间
using SqlSugar;                                          // SqlSugar ORM框架
using SqlsugarService.Application.DTOs.Materials;       // 物料相关的数据传输对象
using SqlsugarService.Application.IService.Material;    // 物料服务接口
using SqlsugarService.Application.Until;                // 工具类（如ApiResult）
using SqlsugarService.Domain.Materials;                 // 物料领域实体
using SqlsugarService.Infrastructure.DbContext;         // 数据库上下文
using SqlsugarService.Infrastructure.IRepository;       // 仓储接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SqlsugarService.Application.Service.Material
{
    /// <summary>
    /// 物料服务实现类
    /// 负责处理物料和物料分类的业务逻辑
    /// </summary>
    public class MaterialService : IMaterialService
    {
        // 私有字段：依赖注入的服务
        private readonly SqlSugarDbContext _dbContext;                      // 数据库上下文，用于直接执行SQL查询
        private readonly IBaseRepository<MaterialCategory> _categoryRepository; // 物料分类仓储，用于分类的CRUD操作
        private readonly IBaseRepository<MaterialEntity> _materialRepository;   // 物料仓储，用于物料的CRUD操作

        /// <summary>
        /// 构造函数：通过依赖注入获取所需的服务
        /// </summary>
        /// <param name="dbContext">数据库上下文</param>
        /// <param name="categoryRepository">物料分类仓储</param>
        /// <param name="materialRepository">物料仓储</param>
        public MaterialService(
            SqlSugarDbContext dbContext,
            IBaseRepository<MaterialCategory> categoryRepository,
            IBaseRepository<MaterialEntity> materialRepository)
        {
            _dbContext = dbContext;
            _categoryRepository = categoryRepository;
            _materialRepository = materialRepository;
        }

        #region 物料分类管理

        /// <summary>
        /// 添加物料分类（支持树形结构）
        /// 功能：创建新的物料分类，支持父子级关系
        /// </summary>
        /// <param name="categoryDto">物料分类数据传输对象，包含要创建的分类信息</param>
        /// <returns>操作结果，成功或失败信息</returns>
        public async Task<ApiResult> AddMaterialCategory(MaterialCategoryDto categoryDto)
        {
            try
            {
                // 步骤1：验证父分类是否存在
                // 如果用户指定了父分类ID，需要检查这个父分类是否真的存在
                if (categoryDto.ParentId.HasValue)
                {
                    // 在数据库中查询是否存在指定的父分类
                    var parentExists = await _dbContext.Db.Queryable<MaterialCategory>()
                        .AnyAsync(c => c.Id == categoryDto.ParentId.Value);

                    // 如果父分类不存在，将ParentId设为空（表示根分类）
                    if (!parentExists)
                    {
                        categoryDto.ParentId = Guid.Empty;
                    }
                }

                // 步骤2：验证同级分类下名称是否重复
                // 在同一个父分类下，不能有重复的分类名称
                var existingSameName = await _dbContext.Db.Queryable<MaterialCategory>()
                    .Where(c => c.Name == categoryDto.Name && c.ParentId == categoryDto.ParentId)
                    .AnyAsync();

                // 如果发现重复名称，返回错误信息
                if (existingSameName)
                {
                    return ApiResult.Fail("同级分类下已存在相同名称", ResultCode.Error);
                }

                // 步骤3：创建新的分类实体对象
                var category = new MaterialCategory
                {
                    Id = Guid.NewGuid(),                    // 生成唯一ID
                    Name = categoryDto.Name,                // 分类名称
                    ParentId = categoryDto.ParentId,        // 父分类ID
                    ParentCategory = null,                  // 父分类对象（暂时为空）
                    Remark = categoryDto.Remark,            // 备注信息
                    CreatedAt = DateTime.Now,               // 创建时间
                    CreatedBy = "system",                   // 创建者（实际应该是当前登录用户）
                    ChildCategories = null,                 // 子分类列表（暂时为空）
                    Materials = null,                       // 物料列表（暂时为空）
                };

                // 步骤4：将新分类保存到数据库
                await _categoryRepository.InsertAsync(category);

                // 步骤5：返回成功结果
                return ApiResult.Success(ResultCode.Success);
            }
            catch (Exception ex)
            {
                // 如果发生异常，返回失败结果和错误信息
                return ApiResult.Fail($"添加分类失败: {ex.Message}", ResultCode.Error);
            }
        }

        /// <summary>
        /// 获取展平的物料分类列表（仅返回ID、名称和父级ID）
        /// 功能：获取所有分类的简化信息，不构建树形结构，适用于下拉选择等场景
        /// </summary>
        /// <returns>包含所有分类简化信息的列表</returns>
        public async Task<ApiResult<List<MaterialCategoryListDto>>> GetFlatMaterialCategories()
        {
            try
            {
                // 从数据库查询所有分类，只选择需要的字段（ID、名称、父级ID）
                // 这样可以减少数据传输量，提高性能
                var categories = await _dbContext.Db.Queryable<MaterialCategory>()
                    .Select(c => new MaterialCategoryListDto  // 只选择需要的字段
                    {
                        Id = c.Id,              // 分类ID
                        Name = c.Name,          // 分类名称
                        ParentId = c.ParentId   // 父分类ID
                    })
                    .OrderBy(c => c.Name)       // 按名称排序
                    .ToListAsync();             // 异步执行查询

                // 返回成功结果和数据
                return ApiResult<List<MaterialCategoryListDto>>.Success(categories, ResultCode.Success);
            }
            catch (Exception ex)
            {
                // 如果查询失败，返回错误信息
                return ApiResult<List<MaterialCategoryListDto>>.Fail($"获取分类列表失败: {ex.Message}", ResultCode.Error);
            }
        }

        /// <summary>
        /// 获取树形结构的物料分类
        /// 功能：将扁平的分类数据构建成树形结构，适用于前端树形控件显示
        /// </summary>
        /// <returns>树形结构的分类数据</returns>
        public async Task<ApiResult<List<MaterialCategoryTreeDto>>> GetMaterialCategoryTree()
        {
            try
            {
                // 步骤1：从数据库获取所有分类的基本信息
                var allCategories = await _dbContext.Db.Queryable<MaterialCategory>()
                    .Select(c => new MaterialCategoryTreeDto
                    {
                        Id = c.Id,              // 分类ID
                        Name = c.Name,          // 分类名称
                        ParentId = c.ParentId   // 父分类ID
                    })
                    .OrderBy(c => c.Name)       // 按名称排序
                    .ToListAsync();

                // 步骤2：筛选出根节点（没有父分类的分类）
                // Guid.Empty 表示没有父分类，即根分类
                var rootCategories = allCategories.Where(c => c.ParentId == Guid.Empty).ToList();

                // 步骤3：递归构建树形结构
                // 为每个根节点找到它的子节点，然后为子节点再找子节点，以此类推
                BuildCategoryTreeDto(rootCategories, allCategories);

                // 步骤4：返回构建好的树形结构
                return ApiResult<List<MaterialCategoryTreeDto>>.Success(rootCategories, ResultCode.Success);
            }
            catch (Exception ex)
            {
                // 如果构建失败，返回错误信息
                return ApiResult<List<MaterialCategoryTreeDto>>.Fail($"获取分类树失败: {ex.Message}", ResultCode.Error);
            }
        }

        /// <summary>
        /// 递归构建分类树DTO
        /// 功能：这是一个递归方法，用于构建树形结构
        /// 原理：对于每个父节点，找到它的所有子节点，然后对子节点重复这个过程
        /// </summary>
        /// <param name="parentNodes">当前层级的父节点列表</param>
        /// <param name="allNodes">所有节点的完整列表</param>
        private void BuildCategoryTreeDto(List<MaterialCategoryTreeDto> parentNodes, List<MaterialCategoryTreeDto> allNodes)
        {
            // 遍历当前层级的每个父节点
            foreach (var parent in parentNodes)
            {
                // 在所有节点中查找当前父节点的子节点
                // 子节点的特征：ParentId 等于当前节点的 Id
                var children = allNodes.Where(n => n.ParentId == parent.Id).ToList();

                // 将找到的子节点赋值给父节点的Children属性
                parent.Children = children;

                // 如果找到了子节点，递归处理这些子节点
                // 这样可以构建多层级的树形结构
                if (children.Any())
                {
                    BuildCategoryTreeDto(children, allNodes);  // 递归调用
                }
            }
        }

        #endregion

        #region 物料管理

        /// <summary>
        /// 添加物料
        /// 功能：创建新的物料记录，包含完整的业务验证逻辑
        /// </summary>
        /// <param name="materialDto">物料数据传输对象，包含要创建的物料信息</param>
        /// <returns>操作结果，成功或失败信息</returns>
        public async Task<ApiResult> AddMaterial(MaterialDto materialDto)
        {
            try
            {
                // 步骤1：验证物料分类是否存在
                // 确保用户选择的分类在数据库中真实存在
                var categoryExists = await _dbContext.Db.Queryable<MaterialCategory>()
                    .AnyAsync(c => c.Id == materialDto.MaterialCategoryId);

                if (!categoryExists)
                {
                    return ApiResult.Fail("物料分类不存在", ResultCode.NotFound);
                }

                // 步骤2：验证物料编号是否重复
                // 物料编号必须唯一，不能重复
                if (!string.IsNullOrEmpty(materialDto.MaterialNumber))
                {
                    var exists = await _dbContext.Db.Queryable<MaterialEntity>()
                        .AnyAsync(m => m.MaterialNumber == materialDto.MaterialNumber);

                    if (exists)
                    {
                        return ApiResult.Fail("物料编号已存在", ResultCode.Error);
                    }
                }

                // 步骤3：库存上下限逻辑验证
                // 库存上限必须大于或等于库存下限，这是业务规则
                if (materialDto.StockUpperLimit.HasValue && materialDto.StockLowerLimit.HasValue)
                {
                    if (materialDto.StockUpperLimit.Value < materialDto.StockLowerLimit.Value)
                    {
                        return ApiResult.Fail("库存上限不能小于库存下限", ResultCode.Error);
                    }
                }

                // 步骤4：价格合理性验证
                // 价格不能为负数，这是常识性的业务规则
                if (materialDto.PurchasePrice.HasValue && materialDto.PurchasePrice.Value < 0)
                {
                    return ApiResult.Fail("采购价格不能为负数", ResultCode.Error);
                }

                if (materialDto.SalesPrice.HasValue && materialDto.SalesPrice.Value < 0)
                {
                    return ApiResult.Fail("销售价格不能为负数", ResultCode.Error);
                }

                // 步骤5：报警天数范围验证
                // 报警天数应该在合理范围内（0-365天）
                if (materialDto.AlarmDays.HasValue && (materialDto.AlarmDays.Value < 0 || materialDto.AlarmDays.Value > 365))
                {
                    return ApiResult.Fail("报警天数必须在0-365之间", ResultCode.Error);
                }

                // 步骤6：创建物料实体对象
                // 将DTO中的数据映射到实体对象中
                var material = new MaterialEntity
                {
                    Id = Guid.NewGuid(),                                    // 生成唯一ID
                    MaterialNumber = materialDto.MaterialNumber,           // 物料编号
                    MaterialName = materialDto.MaterialName,               // 物料名称
                    SpecificationModel = materialDto.SpecificationModel,   // 规格型号
                    Unit = materialDto.Unit,                               // 单位
                    MaterialType = materialDto.MaterialType,               // 物料类型（物料/成品）
                    MaterialProperty = materialDto.MaterialProperty,       // 物料属性（自制/外购等）
                    MaterialCategoryId = materialDto.MaterialCategoryId,   // 所属分类ID
                    Status = materialDto.Status ?? "启用",                 // 状态，默认启用
                    EffectiveDate = materialDto.EffectiveDate,             // 有效日期
                    EffectiveUnit = materialDto.EffectiveUnit,             // 有效期单位
                    AlarmDays = materialDto.AlarmDays,                     // 报警天数
                    StockUpperLimit = materialDto.StockUpperLimit,         // 库存上限
                    StockLowerLimit = materialDto.StockLowerLimit,         // 库存下限
                    PurchasePrice = materialDto.PurchasePrice,             // 采购价格
                    SalesPrice = materialDto.SalesPrice,                   // 销售价格
                    Remarks = materialDto.Remarks,                         // 备注
                    MaterialImage = materialDto.MaterialImage,             // 物料图片URL
                    Attachment = materialDto.Attachment,                   // 附件URL
                    CreatedAt = DateTime.Now,                              // 创建时间
                    CreatedBy = "system"                                   // 创建者（实际应该是当前登录用户）
                };

                // 步骤7：将物料保存到数据库
                await _dbContext.Db.Insertable(material).ExecuteCommandAsync();

                // 步骤8：返回成功结果
                return ApiResult.Success(ResultCode.Success);
            }
            catch (Exception ex)
            {
                // 如果发生异常，返回失败结果和错误信息
                return ApiResult.Fail($"添加物料失败: {ex.Message}", ResultCode.Error);
            }
        }

        /// <summary>
        /// 按分类获取物料(分页)
        /// 功能：根据分类ID获取物料列表，支持分页显示，包含分类信息
        /// </summary>
        /// <param name="categoryId">分类ID，如果为空则获取所有物料</param>
        /// <param name="pageIndex">页码，从1开始</param>
        /// <param name="pageSize">每页显示的记录数</param>
        /// <returns>分页的物料列表</returns>
        public async Task<ApiResult<PageResult<List<MaterialSimpleDto>>>> GetMaterialsByCategory(Guid categoryId, int pageIndex, int pageSize)
        {
            try
            {
                // 步骤1：构建基础查询
                // 创建物料表的查询对象
                var query = _dbContext.Db.Queryable<MaterialEntity>();

                // 如果指定了分类ID，则添加分类过滤条件
                if (categoryId != Guid.Empty)
                    query = query.Where(m => m.MaterialCategoryId == categoryId);

                // 步骤2：获取符合条件的记录总数
                // 这个总数用于计算总页数，必须在分页之前获取
                var totalCount = await query.CountAsync();

                // 步骤3：执行分页查询并关联分类信息
                var data = await query
                    // 左连接物料分类表，获取分类名称
                    // LeftJoin确保即使分类被删除，物料记录也能显示
                    .LeftJoin<MaterialCategory>((m, c) => m.MaterialCategoryId == c.Id)
                    // 选择需要返回的字段，映射到MaterialSimpleDto
                    .Select((m, c) => new MaterialSimpleDto
                    {
                        Id = m.Id,                                                      // 物料ID
                        MaterialNumber = m.MaterialNumber ?? string.Empty,             // 物料编号（空值处理）
                        MaterialName = m.MaterialName ?? string.Empty,                 // 物料名称（空值处理）
                        SpecificationModel = m.SpecificationModel ?? string.Empty,     // 规格型号（空值处理）
                        Unit = m.Unit ?? string.Empty,                                 // 单位（空值处理）
                        MaterialType = m.MaterialType ?? MaterialTypeEnum.Material,   // 物料类型（空值处理）
                        MaterialProperty = m.MaterialProperty ?? string.Empty,         // 物料属性（空值处理）
                        MaterialCategoryId = m.MaterialCategoryId,                     // 分类ID
                        MaterialCategoryName = c.Name ?? string.Empty,                 // 分类名称（从关联表获取）
                        Status = m.Status ?? "启用",                                   // 状态（空值处理）
                    })
                    .OrderBy(m => m.MaterialNumber)                    // 按物料编号排序
                    .Skip((pageIndex - 1) * pageSize)                 // 跳过前面的记录（分页）
                    .Take(pageSize)                                   // 取指定数量的记录（分页）
                    .ToListAsync();                                   // 异步执行查询

                // 步骤4：构建分页结果对象
                var pageResult = new PageResult<List<MaterialSimpleDto>>
                {
                    Data = data,                                                    // 当前页的数据
                    TotalCount = totalCount,                                        // 总记录数
                    TotalPage = (int)Math.Ceiling(totalCount * 1.0 / pageSize)    // 总页数（向上取整）
                };

                // 步骤5：返回成功结果
                return ApiResult<PageResult<List<MaterialSimpleDto>>>.Success(pageResult, ResultCode.Success);
            }
            catch (Exception ex)
            {
                // 如果查询失败，返回错误信息
                return ApiResult<PageResult<List<MaterialSimpleDto>>>.Fail(
                    $"获取物料列表失败: {ex.Message}",
                    ResultCode.Error);
            }
        }

        /// <summary>
        /// 为 MES 工具服务提供的异步方法别名 - 获取物料列表
        /// </summary>
        /// <param name="materialName">物料名称</param>
        /// <param name="materialCode">物料编号</param>
        /// <returns></returns>
        public async Task<ApiResult<PageResult<List<MaterialSimpleDto>>>> GetMaterialListAsync(string? materialName = null, string? materialCode = null)
        {
            try
            {
                // 构建查询
                var query = _dbContext.Db.Queryable<MaterialEntity>();

                // 添加过滤条件
                if (!string.IsNullOrEmpty(materialName))
                    query = query.Where(m => m.MaterialName.Contains(materialName));

                if (!string.IsNullOrEmpty(materialCode))
                    query = query.Where(m => m.MaterialNumber.Contains(materialCode));

                // 获取总数
                var totalCount = await query.CountAsync();

                // 执行查询并关联分类信息
                var data = await query
                    .LeftJoin<MaterialCategory>((m, c) => m.MaterialCategoryId == c.Id)
                    .Select((m, c) => new MaterialSimpleDto
                    {
                        Id = m.Id,
                        MaterialNumber = m.MaterialNumber ?? string.Empty,
                        MaterialName = m.MaterialName ?? string.Empty,
                        SpecificationModel = m.SpecificationModel ?? string.Empty,
                        Unit = m.Unit ?? string.Empty,
                        MaterialType = m.MaterialType ?? MaterialTypeEnum.Material,
                        MaterialProperty = m.MaterialProperty ?? string.Empty,
                        MaterialCategoryId = m.MaterialCategoryId,
                        MaterialCategoryName = c.Name ?? string.Empty,
                        Status = m.Status ?? "启用",
                    })
                    .OrderBy(m => m.MaterialNumber)
                    .Take(100) // 限制返回数量
                    .ToListAsync();

                // 构建分页结果
                var pageResult = new PageResult<List<MaterialSimpleDto>>
                {
                    Data = data,
                    TotalCount = totalCount,
                    TotalPage = (int)Math.Ceiling(totalCount * 1.0 / 100)
                };

                return ApiResult<PageResult<List<MaterialSimpleDto>>>.Success(pageResult, ResultCode.Success);
            }
            catch (Exception ex)
            {
                return ApiResult<PageResult<List<MaterialSimpleDto>>>.Fail(
                    $"获取物料列表失败: {ex.Message}",
                    ResultCode.Error);
            }
        }

        #endregion
    }
}